<!DOCTYPE html>
<!-- saved from url=(0080)https://juejin.im/book/5bdc715fe51d454e755f75ef/section/5bdc7198518825171726cfce -->
<html lang="zh-CN"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no,viewport-fit=cover"><meta name="google-site-verification" content="cCHsgG9ktuCTgWgYfqCJql8AeR4gAne4DTZqztPoirE"><meta name="apple-itunes-app" content="app-id=987739104"><meta name="baidu-site-verification" content="qiK2a1kcFc"><meta name="360-site-verification" content="4c3c7d57d59f0e1a308462fbc7fd7e51"><meta name="sogou_site_verification" content="c49WUDZczQ"><style>body {
        font-size: 16px;
        line-height: 2;
      }
      a, button, input {
        margin: 1rem 1.5rem;
      }
      img {
        width: 0;
        height: 0;
      }
      #juejin {
        overflow-x: hidden;
      }</style><title data-vue-meta="true">前端面试之道 - yck - 掘金小册</title><link rel="apple-touch-icon" sizes="180x180" href="https://b-gold-cdn.xitu.io/favicons/v2/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://b-gold-cdn.xitu.io/favicons/v2/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://b-gold-cdn.xitu.io/favicons/v2/favicon-16x16.png"><link rel="manifest" href="https://b-gold-cdn.xitu.io/favicons/v2/manifest.json"><link rel="mask-icon" href="https://b-gold-cdn.xitu.io/favicons/v2/safari-pinned-tab.svg" color="#5bbad5"><link rel="shortcut icon" href="https://b-gold-cdn.xitu.io/favicons/v2/favicon.ico"><meta name="msapplication-config" content="https://b-gold-cdn.xitu.io/favicons/v2/browserconfig.xml"><meta name="theme-color" content="#ffffff"><link rel="search" title="掘金" href="https://b-gold-cdn.xitu.io/conf/search.xml" type="application/opensearchdescription+xml"><link rel="stylesheet" href="./5-JS 异步编程及常考面试题_files/ionicons.min.css"><link rel="stylesheet" href="./5-JS 异步编程及常考面试题_files/iconfont.css"><link href="./5-JS 异步编程及常考面试题_files/0.20e96f0e16539d696fbd.css" rel="stylesheet"><script async="" src="./5-JS 异步编程及常考面试题_files/hm.js.下载"></script><script async="" src="./5-JS 异步编程及常考面试题_files/analytics.js.下载"></script><script type="text/javascript" async="" src="./5-JS 异步编程及常考面试题_files/vds.js.下载"></script><script charset="utf-8" src="./5-JS 异步编程及常考面试题_files/13.46a6fc9d409a46c2798c.js.下载"></script><meta data-vmid="keywords" name="keywords" content="掘金,稀土,Vue.js,微信小程序,Kotlin,RxJava,React Native,Wireshark,敏捷开发,Bootstrap,OKHttp,正则表达式,WebGL,Webpack,Docker,MVVM" data-vue-meta="true"><meta data-vmid="description" name="description" content="掘金是一个帮助开发者成长的社区，是给开发者用的 Hacker News，给设计师用的 Designer News，和给产品经理用的 Medium。掘金的技术文章由稀土上聚集的技术大牛和极客共同编辑为你筛选出最优质的干货，其中包括：Android、iOS、前端、后端等方面的内容。用户每天都可以在这里找到技术世界的头条内容。与此同时，掘金内还有沸点、掘金翻译计划、线下活动、专栏文章等内容。即使你是 GitHub、StackOverflow、开源中国的用户，我们相信你也可以在这里有所收获。" data-vue-meta="true"></head><body><div id="juejin" data-v-514f306c=""><div class="global-component-box" data-v-514f306c=""><!----><div data-v-71cabb60="" data-v-514f306c="" class="alert-list alert-list"></div><!----><!----><!----><div class="emoji-barrage" data-v-e5f49d52="" data-v-514f306c=""><!----></div><div class="book-new-user-award-popup" style="display:none;" data-v-71b0e7b2="" data-v-514f306c=""><div class="content-box" style="display:;" data-v-71b0e7b2=""><div class="close ion-close-round" data-v-71b0e7b2=""></div><div class="header" data-v-71b0e7b2=""><div class="icon" data-v-71b0e7b2=""><img src="./5-JS 异步编程及常考面试题_files/icon.a87e5ae.svg" data-v-71b0e7b2=""></div><div class="txt" data-v-71b0e7b2="">新人专享好礼</div></div><div class="desc" data-v-71b0e7b2="">凡未购买过小册的用户，均可领取三张 5 折新人专享券，购买小册时自动使用专享券，最高可节省 45 元。</div><div class="tickets" data-v-71b0e7b2=""><div class="ticket" data-v-71b0e7b2=""><div class="ticket__inner" data-v-71b0e7b2=""><div class="enjoy" data-v-71b0e7b2=""><span class="new-title" data-v-71b0e7b2="">小册新人 5 折券</span></div><div class="sale" data-v-71b0e7b2="">最高可省 15 元</div></div></div><div class="ticket" data-v-71b0e7b2=""><div class="ticket__inner" data-v-71b0e7b2=""><div class="enjoy" data-v-71b0e7b2=""><span class="new-title" data-v-71b0e7b2="">小册新人 5 折券</span></div><div class="sale" data-v-71b0e7b2="">最高可省 15 元</div></div></div><div class="ticket" data-v-71b0e7b2=""><div class="ticket__inner" data-v-71b0e7b2=""><div class="enjoy" data-v-71b0e7b2=""><span class="new-title" data-v-71b0e7b2="">小册新人 5 折券</span></div><div class="sale" data-v-71b0e7b2="">最高可省 15 元</div></div></div></div><div class="remark" data-v-71b0e7b2="">注：专享券的使用期限在领券的七天内。</div><div class="submit-btn" data-v-71b0e7b2="">一键领取</div></div><div class="model success" style="display:none;" data-v-71b0e7b2=""><div class="heading" data-v-71b0e7b2="">领取成功</div><div class="content-text" data-v-71b0e7b2="">购买小册时自动使用专享券</div><div class="btn-success-footer" data-v-71b0e7b2=""><div class="btn-ok" data-v-71b0e7b2="">知道了</div><div class="btn-ok btn-link" data-v-71b0e7b2="">前往小册首页</div></div></div><div class="model fail" style="display:none;" data-v-71b0e7b2=""><div class="heading" data-v-71b0e7b2="">领取失败</div><div class="content-text" data-v-71b0e7b2="">本活动仅适用于小册新用户</div><div class="btn-ok" data-v-71b0e7b2="">知道了</div></div></div><!----></div><!----><div data-v-097468bb="" data-v-514f306c="" class="book-read-view"><section data-v-097468bb="" class="book-section"><div data-v-097468bb="" class="book-summary"><div data-v-097468bb="" class="book-summary-masker"></div><div data-v-097468bb="" class="book-summary-inner"><div data-v-097468bb="" class="book-summary__header"><a data-v-097468bb="" href="https://juejin.im/books" target="" rel="" class="logo"><img data-v-097468bb="" src="./5-JS 异步编程及常考面试题_files/logo.a7995ad.svg"></a><div data-v-097468bb="" class="label">小册</div><!----></div><!----><div data-v-d0eb2184="" data-v-097468bb="" class="book-directory book-directory bought"><a data-v-d0eb2184="" class="section section-link read"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">1</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">小册食用指南</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link read"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">2</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">JS 基础知识点及常考面试题（一）</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link read"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">3</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">JS 基础知识点及常考面试题（二）</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link read"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">4</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">ES6 知识点及常考面试题</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section route-active section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">5</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">JS 异步编程及常考面试题</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">6</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">手写 Promise</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">7</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">Event Loop</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">8</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">JS 进阶知识点及常考面试题</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">9</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">JS 思考题</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">10</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">DevTools Tips</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">11</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">浏览器基础知识点及常考面试题</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">12</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">浏览器缓存机制</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">13</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">浏览器渲染原理</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">14</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">安全防范知识点</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">15</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">从 V8 中看 JS 性能优化</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">16</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">性能优化琐碎事</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">17</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">Webpack 性能优化</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">18</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">实现小型打包工具</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">19</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">React 和 Vue 两大框架之间的相爱相杀</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">20</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">Vue 常考基础知识点</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">21</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">Vue 常考进阶知识点</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">22</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">React 常考基础知识点</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">23</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">React 常考进阶知识点</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">24</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">监控</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">25</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">UDP</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">26</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">TCP</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">27</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">HTTP 及 TLS</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">28</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">HTTP/2 及 HTTP/3</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">29</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">输入 URL 到页面渲染的整个流程</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">30</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">设计模式</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">31</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">常见数据结构</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">32</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">常考算法题解析</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">33</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">CSS 常考面试题资料</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">34</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">如何写好一封简历</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">35</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">面试常用技巧</div><!----><!----></div><!----></a><a data-v-d0eb2184="" class="section section-link"><div data-v-d0eb2184="" class="step"><div data-v-d0eb2184="" class="step-btn">36</div></div><div data-v-d0eb2184="" class="center"><div data-v-d0eb2184="" class="title">前方的路，让我们结伴同行</div><!----><!----></div><!----></a></div><div data-v-097468bb="" class="book-summary__footer"><div data-v-097468bb="" class="qr-icon"><img data-v-097468bb="" src="./5-JS 异步编程及常考面试题_files/qr-icon.881015a.svg"></div><div data-v-097468bb="" class="qr-tips"><span data-v-097468bb="" class="ion-close"></span><div data-v-097468bb="" class="title"><span data-v-097468bb="">关注公众号</span><span data-v-097468bb="">领取优惠码</span></div><div data-v-097468bb="" class="qr-img"><img data-v-097468bb="" src="./5-JS 异步编程及常考面试题_files/wx-qr.13d8b4d.png"></div></div></div></div></div><div data-v-097468bb="" class="book-content"><div data-v-097468bb="" class="book-content-inner"><div data-v-097468bb="" class="book-content__header visible"><div data-v-097468bb="" class="switch"><img data-v-097468bb="" src="./5-JS 异步编程及常考面试题_files/icon.3e69d5a.svg"></div><div data-v-097468bb="" class="menu"><img data-v-097468bb="" src="./5-JS 异步编程及常考面试题_files/menu.74b9add.svg"></div><div data-v-097468bb="" class="title"><a data-v-097468bb="" href="https://juejin.im/book/5bdc715fe51d454e755f75ef" target="" rel="">前端面试之道</a></div><div data-v-629272fe="" data-v-097468bb="" class="user-auth user-auth"><div data-v-629272fe="" class="nav-item menu"><div data-v-3b1ba6d2="" data-v-afcc6e34="" data-v-629272fe="" data-src="https://b-gold-cdn.xitu.io/v3/static/img/default-avatar.e30559a.svg" class="lazy avatar avatar loaded" style="background-image: url(&quot;https://b-gold-cdn.xitu.io/v3/static/img/default-avatar.e30559a.svg&quot;);"></div><div data-v-629272fe="" class="nav-menu user-dropdown-list" style="display: none;"><ul data-v-629272fe="" class="nav-menu-item-group"><li data-v-629272fe="" class="nav-menu-item"><a data-v-629272fe=""><i data-v-629272fe="" class="fengwei fw-write"></i><span data-v-629272fe="">写文章</span></a></li><!----><li data-v-629272fe="" class="nav-menu-item"><a data-v-629272fe=""><i data-v-629272fe="" class="fengwei fw-draft"></i><span data-v-629272fe="">草稿</span></a></li></ul><ul data-v-629272fe="" class="nav-menu-item-group"><li data-v-629272fe="" class="nav-menu-item"><a data-v-629272fe="" href="https://juejin.im/user/5c2c54ca6fb9a049cb18da5a"><i data-v-629272fe="" class="fengwei fw-person"></i><span data-v-629272fe="">我的主页</span></a></li><li data-v-629272fe="" class="nav-menu-item"><a data-v-629272fe="" href="https://juejin.im/user/5c2c54ca6fb9a049cb18da5a/likes"><i data-v-629272fe="" class="fengwei fw-like"></i><span data-v-629272fe="">我喜欢的</span></a></li><!----><li data-v-629272fe="" class="nav-menu-item"><a data-v-629272fe="" href="https://juejin.im/user/5c2c54ca6fb9a049cb18da5a/collections"><i data-v-629272fe="" class="fengwei fw-collection"></i><span data-v-629272fe="">我的收藏集</span></a></li><li data-v-629272fe="" class="nav-menu-item"><a data-v-629272fe="" href="https://juejin.im/user/5c2c54ca6fb9a049cb18da5a/books?type=bought"><i data-v-629272fe="" class="fengwei fw-bought"></i><span data-v-629272fe="">已购</span></a></li><!----><li data-v-629272fe="" class="nav-menu-item"><a data-v-629272fe="" href="https://juejin.im/subscribe"><i data-v-629272fe="" class="fengwei fw-tag"></i><span data-v-629272fe="">标签管理</span></a></li></ul><ul data-v-629272fe="" class="nav-menu-item-group"><li data-v-629272fe="" class="nav-menu-item"><a data-v-629272fe="" href="https://juejin.im/user/settings"><i data-v-629272fe="" class="fengwei fw-setting"></i><span data-v-629272fe="">设置</span></a></li><li data-v-629272fe="" class="nav-menu-item more"><a data-v-629272fe=""><i data-v-629272fe="" class="fengwei fw-info"></i><span data-v-629272fe="">关于</span><i data-v-629272fe="" class="ion-chevron-right more-icon"></i></a><div data-v-629272fe="" class="nav-menu more-dropdown-list"><ul data-v-629272fe="" class="nav-menu-item-group"><li data-v-629272fe="" class="nav-menu-item"><a data-v-629272fe="" href="https://juejin.im/app" target="_blank">下载应用</a></li><li data-v-629272fe="" class="nav-menu-item"><a data-v-629272fe="" href="https://juejin.im/about" target="_blank">关于</a></li><li data-v-629272fe="" class="nav-menu-item"><a data-v-629272fe="" href="https://xitu.io/jobs" target="_blank">加入我们</a></li><li data-v-629272fe="" class="nav-menu-item"><a data-v-629272fe="" href="https://github.com/xitu/gold-miner" rel="nofollow noopener noreferrer" target="_blank">翻译计划</a></li><li data-v-629272fe="" class="nav-menu-item"><a data-v-629272fe="" href="https://bd.juejin.im/?utm_campaign=bd&amp;utm_source=web&amp;utm_medium=nav" target="_blank">合作伙伴</a></li></ul></div></li></ul><ul data-v-629272fe="" class="nav-menu-item-group"><li data-v-629272fe="" class="nav-menu-item"><a data-v-629272fe=""><i data-v-629272fe="" class="fengwei fw-logout"></i><span data-v-629272fe="">登出</span></a></li></ul></div></div><!----></div><!----></div><div data-v-097468bb="" class="book-body transition--next"><div data-v-5602b343="" data-v-097468bb="" class="section-view book-section-content"><div data-v-05bbf43a="" data-v-5602b343="" class="section-content"><div data-v-05bbf43a="" class="section-page book-section-view"><div data-v-05bbf43a="" class="entry-content article-content"><h1 class="heading" data-id="heading-0">JS 异步编程及常考面试题</h1>
<p>在上一章节中我们了解了常见 ES6 语法的一些知识点。这一章节我们将会学习异步编程这一块的内容，鉴于异步编程是 JS 中至关重要的内容，所以我们将会用三个章节来学习异步编程涉及到的重点和难点，同时这一块内容也是面试常考范围，希望大家认真学习。</p>
<h2 class="heading" data-id="heading-1">并发（concurrency）和并行（parallelism）区别</h2>
<blockquote class="warning"><p>涉及面试题：并发与并行的区别？
</p></blockquote><p>异步和这小节的知识点其实并不是一个概念，但是这两个名词确实是很多人都常会混淆的知识点。其实混淆的原因可能只是两个名词在中文上的相似，在英文上来说完全是不同的单词。</p>
<p>并发是宏观概念，我分别有任务 A 和任务 B，在一段时间内通过任务间的切换完成了这两个任务，这种情况就可以称之为并发。</p>
<p>并行是微观概念，假设 CPU 中存在两个核心，那么我就可以同时完成任务 A、B。同时完成多个任务的情况就可以称之为并行。</p>
<h2 class="heading" data-id="heading-2">回调函数（Callback）</h2>
<blockquote class="warning"><p>涉及面试题：什么是回调函数？回调函数有什么缺点？如何解决回调地狱问题？
</p></blockquote><p>回调函数应该是大家经常使用到的，以下代码就是一个回调函数的例子：</p>
<pre><code class="hljs js" lang="js">ajax(url, () =&gt; {
    <span class="hljs-comment">// 处理逻辑</span>
})
</code></pre><p>但是回调函数有一个致命的弱点，就是容易写出回调地狱（Callback hell）。假设多个请求存在依赖性，你可能就会写出如下代码：</p>
<pre><code class="hljs js" lang="js">ajax(url, () =&gt; {
    <span class="hljs-comment">// 处理逻辑</span>
    ajax(url1, () =&gt; {
        <span class="hljs-comment">// 处理逻辑</span>
        ajax(url2, () =&gt; {
            <span class="hljs-comment">// 处理逻辑</span>
        })
    })
})
</code></pre><p>以上代码看起来不利于阅读和维护，当然，你可能会想说解决这个问题还不简单，把函数分开来写不就得了</p>
<pre><code class="hljs js" lang="js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">firstAjax</span>(<span class="hljs-params"></span>) </span>{
  ajax(url1, () =&gt; {
    <span class="hljs-comment">// 处理逻辑</span>
    secondAjax()
  })
}
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">secondAjax</span>(<span class="hljs-params"></span>) </span>{
  ajax(url2, () =&gt; {
    <span class="hljs-comment">// 处理逻辑</span>
  })
}
ajax(url, () =&gt; {
  <span class="hljs-comment">// 处理逻辑</span>
  firstAjax()
})
</code></pre><p>以上的代码虽然看上去利于阅读了，但是还是没有解决根本问题。</p>
<p>回调地狱的根本问题就是：</p>
<ol>
<li>嵌套函数存在耦合性，一旦有所改动，就会牵一发而动全身</li>
<li>嵌套函数一多，就很难处理错误</li>
</ol>
<p>当然，回调函数还存在着别的几个缺点，比如不能使用 <code>try catch</code> 捕获错误，不能直接 <code>return</code>。在接下来的几小节中，我们将来学习通过别的技术解决这些问题。</p>
<h2 class="heading" data-id="heading-3">Generator</h2>
<blockquote class="warning"><p>涉及面试题：你理解的 Generator 是什么？
</p></blockquote><p><code>Generator</code> 算是 ES6 中难理解的概念之一了，<code>Generator</code> 最大的特点就是可以控制函数的执行。在这一小节中我们不会去讲什么是 <code>Generator</code>，而是把重点放在 <code>Generator</code> 的一些容易困惑的地方。</p>
<pre><code class="hljs js" lang="js"><span class="hljs-function"><span class="hljs-keyword">function</span> *<span class="hljs-title">foo</span>(<span class="hljs-params">x</span>) </span>{
  <span class="hljs-keyword">let</span> y = <span class="hljs-number">2</span> * (<span class="hljs-keyword">yield</span> (x + <span class="hljs-number">1</span>))
  <span class="hljs-keyword">let</span> z = <span class="hljs-keyword">yield</span> (y / <span class="hljs-number">3</span>)
  <span class="hljs-keyword">return</span> (x + y + z)
}
<span class="hljs-keyword">let</span> it = foo(<span class="hljs-number">5</span>)
<span class="hljs-built_in">console</span>.log(it.next())   <span class="hljs-comment">// =&gt; {value: 6, done: false}</span>
<span class="hljs-built_in">console</span>.log(it.next(<span class="hljs-number">12</span>)) <span class="hljs-comment">// =&gt; {value: 8, done: false}</span>
<span class="hljs-built_in">console</span>.log(it.next(<span class="hljs-number">13</span>)) <span class="hljs-comment">// =&gt; {value: 42, done: true}</span>

</code></pre><p>你也许会疑惑为什么会产生与你预想不同的值，接下来就让我为你逐行代码分析原因</p>
<ul>
<li>首先 <code>Generator</code> 函数调用和普通函数不同，它会返回一个迭代器</li>
<li>当执行第一次 <code>next</code> 时，传参会被忽略，并且函数暂停在 <code>yield (x + 1)</code> 处，所以返回 <code>5 + 1 = 6</code></li>
<li>当执行第二次 <code>next</code> 时，传入的参数等于上一个 <code>yield</code> 的返回值，如果你不传参，<code>yield</code> 永远返回 <code>undefined</code>。此时 <code>let y = 2 * 12</code>，所以第二个 <code>yield</code> 等于 <code>2 * 12 / 3 = 8</code></li>
<li>当执行第三次 <code>next</code> 时，传入的参数会传递给 <code>z</code>，所以 <code>z = 13, x = 5, y = 24</code>，相加等于 <code>42</code></li>
</ul>
<p><code>Generator</code> 函数一般见到的不多，其实也于他有点绕有关系，并且一般会配合 co 库去使用。当然，我们可以通过 <code>Generator</code> 函数解决回调地狱的问题，可以把之前的回调地狱例子改写为如下代码：</p>
<pre><code class="hljs js" lang="js"><span class="hljs-function"><span class="hljs-keyword">function</span> *<span class="hljs-title">fetch</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">yield</span> ajax(url, () =&gt; {})
    <span class="hljs-keyword">yield</span> ajax(url1, () =&gt; {})
    <span class="hljs-keyword">yield</span> ajax(url2, () =&gt; {})
}
<span class="hljs-keyword">let</span> it = fetch()
<span class="hljs-keyword">let</span> result1 = it.next()
<span class="hljs-keyword">let</span> result2 = it.next()
<span class="hljs-keyword">let</span> result3 = it.next()

</code></pre><h2 class="heading" data-id="heading-4">Promise</h2>
<blockquote class="warning"><p>涉及面试题：Promise 的特点是什么，分别有什么优缺点？什么是 Promise 链？Promise 构造函数执行和 then 函数执行有什么区别？
</p></blockquote><p><code>Promise</code> 翻译过来就是承诺的意思，这个承诺会在未来有一个确切的答复，并且该承诺有三种状态，分别是：</p>
<ol>
<li>等待中（pending）</li>
<li>完成了 （resolved）</li>
<li>拒绝了（rejected）</li>
</ol>
<p>这个承诺一旦从等待状态变成为其他状态就永远不能更改状态了，也就是说一旦状态变为 resolved 后，就不能再次改变</p>
<pre><code class="hljs bash" lang="bash">new Promise((resolve, reject) =&gt; {
  resolve(<span class="hljs-string">'success'</span>)
  // 无效
  reject(<span class="hljs-string">'reject'</span>)
})

</code></pre><p>当我们在构造 <code>Promise</code> 的时候，构造函数内部的代码是立即执行的</p>
<pre><code class="hljs js" lang="js"><span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'new Promise'</span>)
  resolve(<span class="hljs-string">'success'</span>)
})
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'finifsh'</span>)
<span class="hljs-comment">// new Promise -&gt; finifsh</span>
</code></pre><p><code>Promise</code> 实现了链式调用，也就是说每次调用 <code>then</code> 之后返回的都是一个 <code>Promise</code>，并且是一个全新的 <code>Promise</code>，原因也是因为状态不可变。如果你在 <code>then</code> 中 使用了 <code>return</code>，那么 <code>return</code> 的值会被 <code>Promise.resolve()</code> 包装</p>
<pre><code class="hljs js" lang="js"><span class="hljs-built_in">Promise</span>.resolve(<span class="hljs-number">1</span>)
  .then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(res) <span class="hljs-comment">// =&gt; 1</span>
    <span class="hljs-keyword">return</span> <span class="hljs-number">2</span> <span class="hljs-comment">// 包装成 Promise.resolve(2)</span>
  })
  .then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(res) <span class="hljs-comment">// =&gt; 2</span>
  })
</code></pre><p>当然了，<code>Promise</code> 也很好地解决了回调地狱的问题，可以把之前的回调地狱例子改写为如下代码：</p>
<pre><code class="hljs js" lang="js">ajax(url)
  .then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> {
      <span class="hljs-built_in">console</span>.log(res)
      <span class="hljs-keyword">return</span> ajax(url1)
  }).then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> {
      <span class="hljs-built_in">console</span>.log(res)
      <span class="hljs-keyword">return</span> ajax(url2)
  }).then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> <span class="hljs-built_in">console</span>.log(res))
</code></pre><p>前面都是在讲述 <code>Promise</code> 的一些优点和特点，其实它也是存在一些缺点的，比如无法取消 <code>Promise</code>，错误需要通过回调函数捕获。</p>
<h2 class="heading" data-id="heading-5">async 及 await</h2>
<blockquote class="warning"><p>涉及面试题：async 及 await 的特点，它们的优点和缺点分别是什么？await 原理是什么？
</p></blockquote><p>一个函数如果加上 <code>async</code> ，那么该函数就会返回一个 <code>Promise</code></p>
<pre><code class="hljs js" lang="js"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-string">"1"</span>
}
<span class="hljs-built_in">console</span>.log(test()) <span class="hljs-comment">// -&gt; Promise {&lt;resolved&gt;: "1"}</span>
</code></pre><p><code>async</code> 就是将函数返回值使用 <code>Promise.resolve()</code> 包裹了下，和 <code>then</code> 中处理返回值一样，并且 <code>await</code> 只能配套 <code>async</code> 使用</p>
<pre><code class="hljs js" lang="js"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">let</span> value = <span class="hljs-keyword">await</span> sleep()
}
</code></pre><p><code>async</code> 和 <code>await</code> 可以说是异步终极解决方案了，相比直接使用 <code>Promise</code> 来说，优势在于处理 <code>then</code> 的调用链，能够更清晰准确的写出代码，毕竟写一大堆 <code>then</code> 也很恶心，并且也能优雅地解决回调地狱问题。当然也存在一些缺点，因为 <code>await</code> 将异步代码改造成了同步代码，如果多个异步代码没有依赖性却使用了 <code>await</code> 会导致性能上的降低。</p>
<pre><code class="hljs js" lang="js"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// 以下代码没有依赖性的话，完全可以使用 Promise.all 的方式</span>
  <span class="hljs-comment">// 如果有依赖性的话，其实就是解决回调地狱的例子了</span>
  <span class="hljs-keyword">await</span> fetch(url)
  <span class="hljs-keyword">await</span> fetch(url1)
  <span class="hljs-keyword">await</span> fetch(url2)
}
</code></pre><p>下面来看一个使用 <code>await</code> 的例子：</p>
<pre><code class="hljs js" lang="js"><span class="hljs-keyword">let</span> a = <span class="hljs-number">0</span>
<span class="hljs-keyword">let</span> b = <span class="hljs-keyword">async</span> () =&gt; {
  a = a + <span class="hljs-keyword">await</span> <span class="hljs-number">10</span>
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'2'</span>, a) <span class="hljs-comment">// -&gt; '2' 10</span>
}
b()
a++
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'1'</span>, a) <span class="hljs-comment">// -&gt; '1' 1</span>
</code></pre><p>对于以上代码你可能会有疑惑，让我来解释下原因</p>
<ul>
<li>首先函数 <code>b</code> 先执行，在执行到 <code>await 10</code> 之前变量 <code>a</code> 还是 0，因为 <code>await</code> 内部实现了 <code>generator</code> ，<code>generator</code> 会保留堆栈中东西，所以这时候 <code>a = 0</code> 被保存了下来</li>
<li>因为 <code>await</code> 是异步操作，后来的表达式不返回 <code>Promise</code> 的话，就会包装成 <code>Promise.reslove(返回值)</code>，然后会去执行函数外的同步代码</li>
<li>同步代码执行完毕后开始执行异步代码，将保存下来的值拿出来使用，这时候 <code>a = 0 + 10</code></li>
</ul>
<p>上述解释中提到了 <code>await</code> 内部实现了 <code>generator</code>，其实 <code>await</code> 就是 <code>generator</code> 加上 <code>Promise</code> 的语法糖，且内部实现了自动执行 <code>generator</code>。如果你熟悉 co 的话，其实自己就可以实现这样的语法糖。</p>
<h2 class="heading" data-id="heading-6">常用定时器函数</h2>
<blockquote class="warning"><p>涉及面试题：setTimeout、setInterval、requestAnimationFrame 各有什么特点？
</p></blockquote><p>异步编程当然少不了定时器了，常见的定时器函数有 <code>setTimeout</code>、<code>setInterval</code>、<code>requestAnimationFrame</code>。我们先来讲讲最常用的<code>setTimeout</code>，很多人认为 <code>setTimeout</code> 是延时多久，那就应该是多久后执行。</p>
<p>其实这个观点是错误的，因为 JS 是单线程执行的，如果前面的代码影响了性能，就会导致 <code>setTimeout</code> 不会按期执行。当然了，我们可以通过代码去修正 <code>setTimeout</code>，从而使定时器相对准确</p>
<pre><code class="hljs js" lang="js"><span class="hljs-keyword">let</span> period = <span class="hljs-number">60</span> * <span class="hljs-number">1000</span> * <span class="hljs-number">60</span> * <span class="hljs-number">2</span>
<span class="hljs-keyword">let</span> startTime = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().getTime()
<span class="hljs-keyword">let</span> count = <span class="hljs-number">0</span>
<span class="hljs-keyword">let</span> end = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().getTime() + period
<span class="hljs-keyword">let</span> interval = <span class="hljs-number">1000</span>
<span class="hljs-keyword">let</span> currentInterval = interval

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">loop</span>(<span class="hljs-params"></span>) </span>{
  count++
  <span class="hljs-comment">// 代码执行所消耗的时间</span>
  <span class="hljs-keyword">let</span> offset = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().getTime() - (startTime + count * interval);
  <span class="hljs-keyword">let</span> diff = end - <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().getTime()
  <span class="hljs-keyword">let</span> h = <span class="hljs-built_in">Math</span>.floor(diff / (<span class="hljs-number">60</span> * <span class="hljs-number">1000</span> * <span class="hljs-number">60</span>))
  <span class="hljs-keyword">let</span> hdiff = diff % (<span class="hljs-number">60</span> * <span class="hljs-number">1000</span> * <span class="hljs-number">60</span>)
  <span class="hljs-keyword">let</span> m = <span class="hljs-built_in">Math</span>.floor(hdiff / (<span class="hljs-number">60</span> * <span class="hljs-number">1000</span>))
  <span class="hljs-keyword">let</span> mdiff = hdiff % (<span class="hljs-number">60</span> * <span class="hljs-number">1000</span>)
  <span class="hljs-keyword">let</span> s = mdiff / (<span class="hljs-number">1000</span>)
  <span class="hljs-keyword">let</span> sCeil = <span class="hljs-built_in">Math</span>.ceil(s)
  <span class="hljs-keyword">let</span> sFloor = <span class="hljs-built_in">Math</span>.floor(s)
  <span class="hljs-comment">// 得到下一次循环所消耗的时间</span>
  currentInterval = interval - offset 
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'时：'</span>+h, <span class="hljs-string">'分：'</span>+m, <span class="hljs-string">'毫秒：'</span>+s, <span class="hljs-string">'秒向上取整：'</span>+sCeil, <span class="hljs-string">'代码执行时间：'</span>+offset, <span class="hljs-string">'下次循环间隔'</span>+currentInterval) 

  setTimeout(loop, currentInterval)
}

setTimeout(loop, currentInterval)
</code></pre><p>接下来我们来看 <code>setInterval</code>，其实这个函数作用和 <code>setTimeout</code> 基本一致，只是该函数是每隔一段时间执行一次回调函数。</p>
<p>通常来说不建议使用 <code>setInterval</code>。第一，它和 <code>setTimeout</code> 一样，不能保证在预期的时间执行任务。第二，它存在执行累积的问题，请看以下伪代码</p>
<pre><code class="hljs js" lang="js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">demo</span>(<span class="hljs-params"></span>) </span>{
  setInterval(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-number">2</span>)
  },<span class="hljs-number">1000</span>)
  sleep(<span class="hljs-number">2000</span>)
}
demo()
</code></pre><p>以上代码在浏览器环境中，如果定时器执行过程中出现了耗时操作，多个回调函数会在耗时操作结束以后同时执行，这样可能就会带来性能上的问题。</p>
<p>如果你有循环定时器的需求，其实完全可以通过 <code>requestAnimationFrame</code> 来实现</p>
<pre><code class="hljs js" lang="js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setInterval</span>(<span class="hljs-params">callback, interval</span>) </span>{
  <span class="hljs-keyword">let</span> timer
  <span class="hljs-keyword">const</span> now = <span class="hljs-built_in">Date</span>.now
  <span class="hljs-keyword">let</span> startTime = now()
  <span class="hljs-keyword">let</span> endTime = startTime
  <span class="hljs-keyword">const</span> loop = <span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> {
    timer = <span class="hljs-built_in">window</span>.requestAnimationFrame(loop)
    endTime = now()
    <span class="hljs-keyword">if</span> (endTime - startTime &gt;= interval) {
      startTime = endTime = now()
      callback(timer)
    }
  }
  timer = <span class="hljs-built_in">window</span>.requestAnimationFrame(loop)
  <span class="hljs-keyword">return</span> timer
}

<span class="hljs-keyword">let</span> a = <span class="hljs-number">0</span>
setInterval(<span class="hljs-function"><span class="hljs-params">timer</span> =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-number">1</span>)
  a++
  <span class="hljs-keyword">if</span> (a === <span class="hljs-number">3</span>) cancelAnimationFrame(timer)
}, <span class="hljs-number">1000</span>)
</code></pre><p>首先 <code>requestAnimationFrame</code> 自带函数节流功能，基本可以保证在 16.6 毫秒内只执行一次（不掉帧的情况下），并且该函数的延时效果是精确的，没有其他定时器时间不准的问题，当然你也可以通过该函数来实现 <code>setTimeout</code>。</p>
<h2 class="heading" data-id="heading-7">小结</h2>
<p>异步编程是 JS 中较难掌握的内容，同时也是很重要的知识点。以上提到的每个知识点其实都可以作为一道面试题，希望大家可以好好掌握以上内容如果大家对于这个章节的内容存在疑问，欢迎在评论区与我互动。</p>
<blockquote class="warning"><p>异步编程相关内容并非一章节就能讲完，需要继续浏览后续章节。
</p></blockquote></div><section data-v-05bbf43a="" class="book-comments"><div data-v-05bbf43a="" class="box-title">留言</div><div data-v-05bbf43a="" class="comment-box"><div data-v-81313b1e="" data-v-05bbf43a="" class="comment-form comment-form" id="comment"><div data-v-3b1ba6d2="" data-v-afcc6e34="" data-v-81313b1e="" data-src="https://b-gold-cdn.xitu.io/v3/static/img/default-avatar.e30559a.svg" class="lazy avatar avatar loaded" style="background-image: url(&quot;https://b-gold-cdn.xitu.io/v3/static/img/default-avatar.e30559a.svg&quot;);"></div><textarea data-v-81313b1e="" placeholder="评论将在后台进行审核，审核通过后对所有人可见" class="content-input" style="overflow: hidden; overflow-wrap: break-word; height: 60px;"></textarea><div data-v-81313b1e="" class="action-box" style="display: none;"><div data-v-680eb36d="" data-v-81313b1e="" class="image-uploader image-uploader" style="display: none;"><input data-v-680eb36d="" type="file" class="input"><button data-v-680eb36d="" class="upload-btn"><i data-v-680eb36d="" class="icon ion-image"></i><span data-v-680eb36d="">上传图片</span></button></div><div data-v-81313b1e="" class="submit-box"><span data-v-81313b1e="" class="submit-text">Ctrl or ⌘ + Enter</span><button data-v-81313b1e="" class="submit-btn">评论</button></div></div><!----></div></div><ul data-v-32e5a55b="" data-v-05bbf43a="" st:block="commentList" class="comment-list comment-list"><!----></ul></section></div></div><!----><!----></div></div><div data-v-a9263a68="" data-v-097468bb="" class="book-handle book-direction"><div data-v-a9263a68="" class="step-btn step-btn--prev"><img data-v-a9263a68="" src="./5-JS 异步编程及常考面试题_files/prev.87ad47e.svg"></div><div data-v-a9263a68="" class="step-btn step-btn--next"><img data-v-a9263a68="" src="./5-JS 异步编程及常考面试题_files/next.54d8a35.svg"></div><!----><!----></div><!----></div></div></section><!----><!----><!----><div data-v-1b0b9cb8="" data-v-097468bb="" class="image-viewer-box"><!----></div></div><!----></div>
      
      
      <script type="text/javascript" src="./5-JS 异步编程及常考面试题_files/runtime.5902a8b1cc2b7567f479.js.下载"></script><script type="text/javascript" src="./5-JS 异步编程及常考面试题_files/0.e205fcdd4d4b6767f462.js.下载"></script><script type="text/javascript" src="./5-JS 异步编程及常考面试题_files/1.cbb1f8f5dcdee0086c6a.js.下载"></script>
    </body></html>